package com.jetair.jap.safe.interceptor;

import com.jetair.jap.booking.common.EncryptUtils;
import com.jetair.jap.safe.FieldAllEncryption;
import com.jetair.jap.safe.FieldPartialEncryption;
import com.jetair.jap.safe.HasSensitiveData;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.*;
import java.lang.reflect.Parameter;

/**
 * 用于处理请求密参数字段加密
 */
@Component
@Intercepts({
        //@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})
})
public class ParameterInterceptor implements Interceptor {
    private static final Logger log = Logger.getLogger("ParameterInterceptor2");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        try {
            if (invocation.getTarget() instanceof Executor) {
                MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
                Object parameterObject = invocation.getArgs()[1];
                if (parameterObject != null) {
                    //多个字段： 1.多个参数2.Map类型
                    if (parameterObject instanceof Map) {
                        FieldPartialEncryption fieldPartialEnCryption = getMethodMapAnnotated(mappedStatement);
                        Map map = (Map) parameterObject;
                        for (Object key : map.keySet()) {
                            Object value = map.get(key);
                            if (value != null){
                                 if (value instanceof String) {
                                     //解析方法上的注解
                                     if (isMethodAnnotated(key, fieldPartialEnCryption, map, (String) value)) {
                                         continue;
                                     }
                                     //解析参数上的注解
                                     if (isParameterAnnotated(mappedStatement, (String) key)) {
                                        String encryptedItem = EncryptUtils.encrypt((String) value);
                                        map.put(key, encryptedItem);
                                    }
                                }
                            }
                        }
                    } else if (parameterObject instanceof String) {//单个字符串
                        if (isSingleParameterAnnotated(mappedStatement)) {
                            String encryptedItem = EncryptUtils.encrypt((String) parameterObject);
                            invocation.getArgs()[1] = encryptedItem;
                        }
                    }
                }
                // 继续执行原始方法
                return invocation.proceed();
            }
        } catch (Exception e) {
            log.error("加密参数处理发生异常："+e.getMessage(),e);
        }
        // 如果不是Executor，继续执行原始方法
        return invocation.proceed();
    }

    private static boolean isMethodAnnotated(Object key, FieldPartialEncryption fieldPartialEnCryption, Map map, String value) {
        if (!Objects.isNull(fieldPartialEnCryption) && map.size()>1) {
            KeyWordsEnum keyWordsEnum = fieldPartialEnCryption.keyWords();
            String key1 = fieldPartialEnCryption.key();
            String value1 = fieldPartialEnCryption.value();
            if (StringUtils.isBlank(key1) || StringUtils.isBlank(value1) || StringUtils.isBlank(keyWordsEnum.getKeywords())) {
                return true;
            }
            List<String> keywords = Arrays.asList(keyWordsEnum.getKeywords().split(","));
            String value2 = value;
            boolean b = keywords.stream().anyMatch(e -> value2.contains(e));

            if(key1.equals(key) && b){
                if(null!= map.get(value1) && map.get(value1) instanceof String){
                    map.put(value1,EncryptUtils.encrypt((String) map.get(value1)));
                }
            }
        }
        return false;
    }


    private boolean isParameterAnnotated(MappedStatement mappedStatement, String parameterName) {
        // 获取方法参数上的注解
        Method method = getMethod(mappedStatement);
        if (method == null) {
            return false;
        }
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        if (parameterAnnotations.length == 0) {
            return false;
        }
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
                if (annotation instanceof FieldAllEncryption) {
                    boolean flag = false;
                    //判断是否有@param,没有@param，直接判断参数名和当前参数是否相等
                    //有@param  判断@param里的value值是否和当前参数相等（不能直接判断参数名，因为@param可能value为其他与当前参数名不一定一致）
                    for (Annotation annotation1 : parameterAnnotations[i]) {
                        if (annotation1 instanceof Param) {
                            Param param = (Param)annotation1;
                            flag = parameterName.equals(param.value());
                            if (flag){
                                return flag;
                            }
                        }
                    }
                    //param的value不相等再判断参数名
                    return parameterName.equals(parameters[i].getName());
                }
            }
        }
        return false;
    }

    private FieldPartialEncryption getMethodMapAnnotated(MappedStatement mappedStatement) {
        Method method = getMethod(mappedStatement);
        if (method == null) {
            return null;
        }
        HasSensitiveData annotation = method.getAnnotation(HasSensitiveData.class);
        if (annotation == null) {
            return null;
        }
        return method.getAnnotation(FieldPartialEncryption.class);
        /*Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        if (parameterAnnotations.length == 0) {
            return null;
        }
        Parameter[] parameters = method.getParameters();
        FieldPartialEncryption fieldPartialEncryption = null;
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
                if (annotation instanceof FieldPartialEncryption) {
                    boolean flag = false;
                    FieldPartialEncryption annotation1 = (FieldPartialEncryption) annotation;
                    if (annotation1.key().equals(parameterName)) {
                        return (FieldPartialEncryption) annotation;
                    }
                }
            }
        }
        return null;*/
    }
    private boolean isSingleParameterAnnotated(MappedStatement mappedStatement) {
        // 获取方法参数上的注解
        Method method = getMethod(mappedStatement);
        if (method == null) {
            return false;
        }
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        if (parameterAnnotations.length == 0) {
            return false;
        }
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameterAnnotations.length; i++) {
            for (Annotation annotation : parameterAnnotations[i]) {
                if (annotation instanceof FieldAllEncryption) {
                    return true;
                }
            }
        }
        return false;
    }

    private Method getMethod(MappedStatement mappedStatement) {
        try {
            // 获取MappedStatement的id，格式为namespace.id
            String statementId = mappedStatement.getId();
            // 获取namespace和id
            int lastDot = statementId.lastIndexOf(".");
            String namespace = statementId.substring(0, lastDot);
            String methodId = statementId.substring(lastDot + 1);
            // 获取接口类型
            Class<?> mapperInterface = Class.forName(namespace);
            // 获取接口方法
            for (Method method : mapperInterface.getDeclaredMethods()) {
                if (method.getName().equals(methodId)) {
                    return method;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {

    }
    @PostConstruct
    public void init() {
        System.out.println("MyBatisConfig has been initialized");
    }
}
